using System.Collections.Generic;
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;

namespace BNode.Expressions
{
    /// <summary>
    /// 用于ExprBuilder对Lambda表达式的处理：
    /// 1.将表达式中的变量替换为指定域中的变量;
    /// 2.自动进行参数类型的适配，例如int和float。
    /// </summary>
    internal class BuilderVisitor : ExpressionVisitor
    {
        protected readonly IEnumerable<ParameterExpression> _scopeParams;
        protected readonly IEnumerable<ParameterExpression> _scopeVars;
        protected IEnumerable<ParameterExpression> Scope => _scopeParams.Concat(_scopeVars);

        private bool _isRoot = true;

        public BuilderVisitor(IEnumerable<ParameterExpression> parameters, IEnumerable<ParameterExpression> variables)
        {
            _scopeParams = parameters;
            _scopeVars = variables;
        }
        public BuilderVisitor(ExprParametersInfo exprParameterInfo)
        {
            _scopeParams = exprParameterInfo.SelfAndRefParameters;
            _scopeVars = exprParameterInfo.SelfAndRefVariables;
        }
        //自定义方法，在全局访问结束时执行，适合需要做一些最终处理的情况
        protected virtual Expression OnVisitEnd(Expression node)
        {
            return node;
        }
        //每个表达式都会先执行Visit，然后分配到具体的Visit方法中
        public override Expression Visit(Expression node)
        {
            if (_isRoot)
            {
                _isRoot = false;
                var end = base.Visit(node);
                return OnVisitEnd(end);
            }
            return base.Visit(node);
        }
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            var method = node.Method;

            var needParameters = node.Method.GetParameters();
            var givenParameters = node.Arguments;

            if (needParameters.Length != givenParameters.Count)
            {
                new ArgumentException($"参数数量不匹配: 预期{needParameters.Length}，实际{givenParameters.Count}").Throw();
                return null;
            }

            var needObjectType = method.DeclaringType;
            var givenObject = node.Object;
            if (givenObject != null)
            {
                givenObject = base.Visit(node.Object);
                givenObject = givenObject.EnsureTypeCompatibility(needObjectType);
            }

            int count = -1;
            var newParameters = needParameters.Zip(givenParameters, (need, given) =>
            {
                count++;
                var after = VisitMethodCallArgument(need, given, method,count);
                return after;
            });

            return method.IsStatic ? Expression.Call(method, newParameters) : Expression.Call(givenObject, method, newParameters);
        }
        protected virtual Expression VisitMethodCallArgument(ParameterInfo need, Expression given, MethodInfo fromMethod,int paramIndex)
        {
            //先拿到参数/解析到最小的Call层级
            var visited = base.Visit(given);
            //尝试解析构建表达式
            if (need.ParameterType == visited.Type) return visited;
            var after = visited.EnsureTypeCompatibility(need.ParameterType);
            return after;
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Type == typeof(ParameterExpression))
            {
                var visitedArg = Scope.FirstOrDefault(v => v.Name == node.Member.Name);
                if (visitedArg != null) return visitedArg;
            }
            return base.VisitMember(node);
        }
        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            var visitedExpressions = node.Expressions.Select(e =>
            {
                e = base.Visit(e);
                return e.EnsureTypeCompatibility(node.Type.GetElementType());
            });
            return node.Update(visitedExpressions);
        }

    }
    internal class DebugBuilderVisitor : BuilderVisitor
    {
        private IndentLogger _debuger;
        public DebugBuilderVisitor(IEnumerable<ParameterExpression> parameters, IEnumerable<ParameterExpression> variables) : base(parameters, variables)
        {
            _debuger = new IndentLogger();
        }
        public DebugBuilderVisitor(ExprParametersInfo exprParameterInfo):base(exprParameterInfo)
        {
            _debuger = new IndentLogger();
        }
        protected override Expression OnVisitEnd(Expression node)
        {
            //尝试输出残余内容
            _debuger.LogOut();
            return base.OnVisitEnd(node);
        }
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            var method = node.Method;
            _debuger.AppendLine($"VisitMethodCall:{method.Name}");
            _debuger.Indent.IncreaseIndent(2);
            return base.VisitMethodCall(node);
        }
        protected override Expression VisitMethodCallArgument(ParameterInfo need, Expression given, MethodInfo fromMethod, int paramIndex)
        {
            var debugTip = $"Method_{fromMethod.Name} Param_{paramIndex}";
            _debuger.AppendLine($"{debugTip} need:{need.ParameterType.GetSimplifyTypeName()} given:{given.NodeType}->{given.Type.GetSimplifyTypeName()}");
            //先拿到参数/解析到元Call层级
            var visited = base.Visit(given);
            _debuger.AppendLine($"{debugTip} visited:{visited.NodeType}->{visited.Type.GetSimplifyTypeName()}");
            //尝试解析构建表达式
            _debuger.AppendLine($"{debugTip} visited compiled:{visited.NodeType}->{visited.Type.GetSimplifyTypeName()}");
            if (need.ParameterType == visited.Type) return visited;
            var after = visited.EnsureTypeCompatibility(need.ParameterType);
            _debuger.AppendLine($"{debugTip} after:{after.NodeType}->{after.Type.GetSimplifyTypeName()}");
            return after;
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            _debuger.AppendLine($"VisitMember:{node.Member.Name}");
            return base.VisitMember(node);
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            _debuger.AppendLine($"VisitParameter:{node.Name}->{node.Type.GetSimplifyTypeName()}");
            return base.VisitParameter(node);
        }
        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            _debuger.AppendLine($"VisitNewArray:{node.Type}");
            return base.VisitNewArray(node);
        }
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            _debuger.LogOut($"VisitLambda Expression<{typeof(T).GetSimplifyTypeName()}>:{node.Name}");
            var newParams = node.Parameters.Where(p => !Scope.Contains(p));
            if (newParams.Count() != 0)
            {
                _debuger.AppendLine($"Lambda Para {string.Join(',', newParams.Select(p => p.ToString()))}");
            }
            return base.VisitLambda(node);
        }
    }

}
